package com.ruoyi.mobile.utils.h5;

import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.mobile.utils.WxCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class CommenUtil {
    // 凭证获取（GET）——access_token
    public final static String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
    // 微信JSSDK的ticket请求URL地址——jsapi_ticket
    public final static String JSAPI_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
    public final String JSON_TOKEN = "access_token";
    public final String JSON_EXPIRESIN = "expires_in";
    public final String REDIS_ACCESSTOKEN = "redisAccessToken";
    public final String REDIS_JSAPI_TICKET = "redisTicket";
    @Autowired
    private RedisTemplate redisTemplate;

    private static CommenUtil instance;
    public static CommenUtil getInstance() {
        if (instance == null) {
            instance = new CommenUtil();
        }
        return instance;
    }

    /**
     * 获取微信验签
     *
     * @param url
     * @return
     */
    public Map<String, String> getWxInitMap(String url, String appid, String secret) {
        try {
            String access_token = getRedisToken(appid, secret);
            if (StringUtils.isBlank(access_token)) {
                log.error("获取token失败");
                return null;
            }
            log.info("access_token：" + access_token);

            String ticket = getRedisTicket(access_token);
            if (StringUtils.isBlank(ticket)) {
                log.error("获取ticket失败");
                return null;
            }
            log.info("获取ticket：" + ticket);
            Map<String, String> resultMap = Sign.sign(ticket, url, appid);
            if (resultMap != null && !resultMap.isEmpty()) {
                log.info("Sign：" + resultMap);
                return resultMap;
            }
            log.error("获取Sign失败");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 发送https请求
     *
     * @param requestUrl    请求地址
     * @param requestMethod 请求方式
     * @param outputStr     提交的数据
     * @return
     */
    private static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) {
        JSONObject jsonObject = null;
        try {
            // 创建SSLContext对象，并使用我们指定的信任管理器初始化
            TrustManager[] tm = {new MyX509TrustManager()};
            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
            sslContext.init(null, tm, new java.security.SecureRandom());

            // 从上述SSLContext对象中得到SSLSocketFactory对象
            SSLSocketFactory ssf = sslContext.getSocketFactory();
            URL url = new URL(requestUrl);
            MyX509TrustManager.ignoreSsl();
            HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
            conn.setSSLSocketFactory(ssf);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            // 设置请求方式（GET/POST）
            conn.setRequestMethod(requestMethod);

            // 当outputStr不为null时向输出流写数据
            if (null != outputStr) {
                OutputStream outputStream = conn.getOutputStream();
                // 注意编码格式
                outputStream.write(outputStr.getBytes("UTF-8"));
                outputStream.close();
            }

            // 从输入流读取返回内容
            InputStream inputStream = conn.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String str = null;
            StringBuffer buffer = new StringBuffer();
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }

            // 释放资源
            bufferedReader.close();
            inputStreamReader.close();
            inputStream.close();
            inputStream = null;
            conn.disconnect();
            jsonObject = JSONObject.parseObject(buffer.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return jsonObject;
    }

    /**
     * 获取accessToken
     *
     * @param appid
     * @param appsecret
     * @return
     */
    public String getAccessToken(String appid, String appsecret) {
        String access_token = "";
        if (null==appsecret||"".equals(appsecret)) {
            return access_token;
        }
        JSONObject jsonObject = null;
        String requestUrl = ACCESS_TOKEN_URL.replace("APPID", appid).replace("APPSECRET", appsecret);
        System.out.println(requestUrl);
        // 发起GET请求获取凭证
        try {
            jsonObject = httpsRequest(requestUrl, "GET", null);
            if (jsonObject != null) {
                try {
                    System.out.println(jsonObject);
                    access_token = jsonObject.getString(JSON_TOKEN);
                    System.out.println(access_token);
                    Integer integer = jsonObject.getInteger(JSON_EXPIRESIN);
                    System.out.println(integer);
                    System.out.println("test:"+REDIS_ACCESSTOKEN);
                    redisTemplate.opsForValue().setIfAbsent(REDIS_ACCESSTOKEN, access_token, integer, TimeUnit.SECONDS);
                } catch (JSONException e) {
                    log.error("获取token失败， errcode：" + jsonObject.getInteger("errcode") + "errmsg:" + jsonObject.getString("errmsg"));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return access_token;
    }

    /**
     * 获取jsapi_ticket
     *
     * @param access_token
     * @return
     */
    private String getTicket(String access_token) {
        log.info("此时的access_token：" + access_token);
        String ticket = "";
        String requestUrl = JSAPI_TICKET_URL.replace("ACCESS_TOKEN", access_token);
        try {
            JSONObject jsonObject = httpsRequest(requestUrl, "GET", null);
            if (jsonObject != null) {
                try {
                    ticket = jsonObject.getString("ticket");
                    redisTemplate.opsForValue().setIfAbsent(REDIS_JSAPI_TICKET, ticket, Integer.parseInt(jsonObject.getString(JSON_EXPIRESIN)), TimeUnit.SECONDS);
                } catch (JSONException e) {
                    access_token = null;
                    log.error("获取ticket失败， errcode：" + jsonObject.getInteger("errcode") + "errmsg:" + jsonObject.getString("errmsg"));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ticket;
    }

    /**
     * 实际获取token，先从redis中查询，如果没有，再重新获取
     *
     * @param appid
     * @param appsecret
     * @return
     */
    private String getRedisToken(String appid, String appsecret) {
        Object oldDate = redisTemplate.opsForValue().get(REDIS_ACCESSTOKEN);
        System.out.println("oldDate:"+oldDate);
        if (null!=oldDate) {
            log.info("没过期，使用redis中的accessToken");
            return redisTemplate.opsForValue().get(REDIS_ACCESSTOKEN).toString();
        } else {
            log.info("过期，重新获取accessToken");
            return getAccessToken(appid, appsecret);
        }
    }

    /**
     * 实际获取ticket，先从redis中查询，如果没有，再重新获取
     *
     * @param access_token
     * @return
     */
    private String getRedisTicket(String access_token) {
        Object oldDate = redisTemplate.opsForValue().get(REDIS_JSAPI_TICKET);
        System.out.println("oldDate:"+oldDate);
        if (null!=oldDate) {
            log.info("没过期，使用Redis中的ticket");
            return redisTemplate.opsForValue().get(REDIS_JSAPI_TICKET).toString();
        } else {
            log.info("过期，重新获取JsapiTicket");
            return getTicket(access_token);
        }
    }
}
